//
//  ViewController.m
//  底层的TCP_Socket连接
//
//  Created by Hello Cai on 2019/11/29.
//  Copyright © 2019 Hello Cai. All rights reserved.
//

#import "ViewController.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>

@interface ViewController ()
@property(nonatomic, assign)int clientSocket;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    /*案例一是tcp socket的客户端实现*/
//    [self tcpSocetDemo];
    
    /*案例二是用客户端tcp socket模拟http请求*/
    [self httpRequest];
}

//===================案例二=====================
-(void)httpRequest{
    int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    self.clientSocket = clientSocket;
    
    //向百度发送GET请求
    [self connectPort:80 addr:"220.181.38.148"];
    NSString *request = @"GET / HTTP/1.1\n"
    "Host: www.baidu.com\n\n";
    
    [self sendAndRecv:request];
    
    //获取到的百度返回结果如下：
    /*
     接收到的消息:HTTP/1.1 200 OK
     Date: Sun, 01 Dec 2019 04:45:28 GMT
     Server: Apache
     Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
     ETag: "51-47cf7e6ee8400"
     Accept-Ranges: bytes
     Content-Length: 81
     Cache-Control: max-age=86400
     Expires: Mon, 02 Dec 2019 04:45:28 GMT
     Connection: Keep-Alive
     Content-Type: text/html
     */
}

-(void)connectPort:(int)port addr:(const char *)addr{
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);    //端口
    serverAddr.sin_addr.s_addr = inet_addr(addr);//地址
    //int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
    int connetResult = connect(_clientSocket, (const struct sockaddr *)&serverAddr, sizeof(serverAddr));
    if (connetResult==0) {
        NSLog(@"连接成功");
    }else{
        NSLog(@"连接失败:%d", connetResult);
    }
}

-(void)sendAndRecv:(NSString *)request{
    NSString *sendMsg = request;
    ssize_t sendLen = send(_clientSocket, sendMsg.UTF8String, strlen(sendMsg.UTF8String), 0);
    NSLog(@"发送了 %ld个字节", sendLen);
    
    uint8_t buffer[1024];
    ssize_t recvLen = recv(_clientSocket, buffer, sizeof(buffer), 0);
    NSLog(@"接收到 %ld个字节数", recvLen);
    
    //把字节数组转换成字符串
    NSData *data = [NSData dataWithBytes:buffer length:recvLen];
    NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到的消息:%@", str);
    
    //5.关闭连接
    close(_clientSocket);
}

//===================案例一=====================
-(void)tcpSocetDemo{
    //1.创建socket
    /**
     domain:  协议域， AF_INET   -->  IPV4
     type:    Socket类型， SOCK_STREAM(TCP)/SOCK_DGRAM(UDP)
     protocol: IPPROTO_TCP 如果传入0，会自动根据第二个参数，选择合适的协议
     返回值，如果创建成功返回的是socket的描述符，失败返回-1
     */
    //int socket(int domain, int type, int protocol);
    int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    
    
    //2.连接到服务器
    /**
     参数
     1>客户端socket
     2>指向数据结构sockaddr的指针，其中包括目的端口和IP地址
     3>结构体数据长度
     返回值：0为连接成功，其他值则连接失败，会有相应的错误代号
     */
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(12345);    //端口
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");//地址
    //int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
    int connetResult = connect(clientSocket, (const struct sockaddr *)&serverAddr, sizeof(serverAddr));
    
    if (connetResult==0) {
        NSLog(@"连接成功");
    }else{
        NSLog(@"连接失败:%d", connetResult);
    }
    
    /** 一、判断是否连接成功
     if (0 == connetResult) {
     //        打开本机模拟服务器端口
     //        打开Netcat 模拟服务器
     //        nc -lk 12345
     //        nc-->Netcat终端下用于调试和检查网络的工具
     NSLog(@"成功");
     } else {
     //        服务器未打开时连接失败
     NSLog(@"失败");
     }
     */
    
    //3.发送数据给服务器
    /**
     参数
     1>客户端socket
     2>发送内容地址
     3>发送内容长度
     4>发送方式标志，一般为0
     返回值：如果成功，则返回发送的字节数。如果失败，则返回SOCKET_ERROR
     */
    NSString *sendMsg = @"Hello";
    ssize_t sendLen = send(clientSocket, sendMsg.UTF8String, strlen(sendMsg.UTF8String), 0);
    NSLog(@"发送了 %ld个字节", sendLen);
    
    //4.从服务器接收数据
    /**
     参数
     1> 客户端socket
     2> 接收内容缓冲区地址
     3> 接收内容缓冲区长度
     4> 接收方式, 0表示阻塞，必须等待服务器返回数据
     返回值：如果成功，则返回读入的字节数。如果失败，则返回SOCKET_ERROR
     */
    uint8_t buffer[1024];
    ssize_t recvLen = recv(clientSocket, buffer, sizeof(buffer), 0);
    NSLog(@"接收到 %ld个字节数", recvLen);
    
    //把字节数组转换成字符串
    NSData *data = [NSData dataWithBytes:buffer length:recvLen];
    NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到的消息:%@", str);
    
    //5.关闭连接
    close(clientSocket);
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end
